﻿using System;
using System.Diagnostics;
using System.IO;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;
using System.Collections.Generic;
using Path = System.Windows.Shapes.Path;
using System.Windows.Controls.Primitives;

namespace IMap
{
    /// <summary>
    /// MapContainer.xaml 的交互逻辑
    /// </summary>
    /// 
    public enum MapType : int
    {
        web = 44,
        sate = 46,
        trans = 47,
    }

    public partial class MapContainer : Canvas
    {
        Popup pop;
        const int TILESIZE = 256;

        string base_url = System.AppDomain.CurrentDomain.BaseDirectory + @"MapData\Baidu\";//配置文件所在目录，注意要和当前工作目录区别开来;
        string web_version;
        string cache_url;
        double W, H;
        bool is_showGrid;

        MapType web_map;
        LatLong centerLatLong;
        PixelPoint BeforeMoveCenterPoint, mouseRtPoint;
        Point mouseLtPoint;
        TextBlock distText;

        public int MinLevel = 2, MaxLevel = 18;
        public int Level { get; set; }

        public bool IsLTClicked { get; set; }
        public bool IsSurveying { get; set; }
        public bool IsMoving { get; set; }
        public bool IsShowGrid
        {
            get
            {
                return is_showGrid;
            }
            set
            {
                is_showGrid = value;
                if (is_showGrid == true)
                {
                    ShowGrid();
                }
                else
                {
                    layers_manager.Remove(LayerType.Grid);
                }
            }
        }

        public CacheType MapCache;
        public MapType WebMap
        {
            get { return web_map; }
            set
            {
                web_map = value;
                switch (web_map)
                {
                    case MapType.web:
                        web_version = "014";
                        cache_url = base_url + value.ToString() + @"\";
                        break;
                    case MapType.sate:
                        web_version = "009";
                        cache_url = base_url + value.ToString() + @"\";
                        break;
                    case MapType.trans:
                        web_version = "014";
                        cache_url = base_url + value.ToString() + @"\";
                        break;
                    default:
                        web_version = string.Format("{0:000}", 14);
                        cache_url = base_url + value.ToString() + @"\";
                        break;
                }

            }
        }
        public List<Point> list_pt = new List<Point>();
        public LayersManager layers_manager;
        public LatLong CenterLatlong
        {
            get
            {
                return centerLatLong;
            }
            set
            {
                centerLatLong = value;
                CenterMercatorPoint = Core.LatLong2Mercator(centerLatLong);
            }
        }
        public MercatorPoint CenterMercatorPoint;

        public MapContainer()
        {
            InitializeComponent();
            MapCache = CacheType.CacheAndServer;
            WebMap = MapType.sate;
            layers_manager = new LayersManager(this);
            IsMoving = true;
            IsSurveying = false;
            IsLTClicked = false;

            //设置显示距离的控件
            distText = new TextBlock();
            distText.FontFamily = new FontFamily("Cabira");
            distText.FontWeight = FontWeights.ExtraBold;
            distText.Foreground = Brushes.OrangeRed;
            distText.Background = Brushes.LightGray;
            this.Children.Add(distText);
            Canvas.SetZIndex(distText, 888);
            distText.Visibility = Visibility.Hidden;
        }

        public void ScanPoint()
        {
            if (layers_manager.Contains(LayerType.Radar))
            {
                layers_manager.Remove(LayerType.Radar);
            }
            else
            {
                layers_manager.Add(LayerType.Radar);
            }

            Radar radar = new Radar();
            radar.Height = 300;
            radar.Width = 300;
            double x = this.Width / 2;
            double y = this.Height / 2 - radar.Height;
            layers_manager.Items(LayerType.Radar).Add(radar, x, y);
        }

        public void ScanPoint(int distance)
        {

            if (layers_manager.Contains(LayerType.Radar))
            {
                layers_manager.Remove(LayerType.Radar);
            }
            else
            {
                layers_manager.Add(LayerType.Radar);
            }
            Radar radar = new Radar();
            radar.Height = distance / Core.GroundResolution(Level);
            radar.Width = distance / Core.GroundResolution(Level);
            double x = this.Width / 2;
            double y = this.Height / 2 - radar.Height;
            layers_manager.Items(LayerType.Radar).Add(radar, x, y);
        }

        public void RedrawMap()
        {
            LayerType layer = new LayerType();
            switch (WebMap)
            {
                case MapType.web:
                    layer = LayerType.Map;
                    break;
                case MapType.sate:
                    WebMap = MapType.trans;
                    layer = LayerType.Trans;
                    ShowMap(layer);
                    WebMap = MapType.sate;
                    layer = LayerType.Map;
                    break;
                default:
                    break;
            }
            ShowMap(layer);
            ShowGrid();
        }

        private void ShowMap(LayerType layer)
        {

            W = this.Width;
            H = this.Height;
            PixelPoint CenterPixelPoint = Core.Mercator2Pixel(CenterMercatorPoint, Level);
            if (layers_manager.Contains(layer))
            {
                layers_manager.Remove(layer);

            }
            else
            {
                layers_manager.Add(layer);
            }

            TileMatrix tileMatrix = new TileMatrix(W, H, CenterPixelPoint);
            Tile[,] tiles = new Tile[tileMatrix.Width, tileMatrix.Height];
            IImage[,] imgSet = new IImage[tileMatrix.Width, tileMatrix.Height];
            String[,] urls = new String[tileMatrix.Width, tileMatrix.Height];
            for (int i = 0; i < tileMatrix.Width; i++)
            {
                for (int j = 0; j < tileMatrix.Height; j++)
                {
                    tiles[i, j] = new Tile(tileMatrix.MinX + i, tileMatrix.MinY + j);
                    imgSet[i, j] = new IImage();
                    double x = (tiles[i, j].Column) * TILESIZE - CenterPixelPoint.X + W / 2;
                    double y = -(tiles[i, j].Row + 1) * TILESIZE + CenterPixelPoint.Y + H / 2;
                    layers_manager.Items(layer).Add(imgSet[i, j], x, y);
                    Debug.Print(@"显示区坐标(X=" + x.ToString() + " ,Y=" + y.ToString() + @")");
                    //String url = @"http://q1.baidu.com/it/u=x=3304;y=888;z=14;v=014;type=web&fm=44";
                    //Z代表缩放级别，取值范围为1-19；v代表地图版本，取值范围011-014，目前最新版本为014；type代表地图类型，取值范围web、sat
                    //fm含义未知
                    //注意服务器的编号应重构！
                    urls[i, j] = string.Format("http://q1.baidu.com/it/u=x={0};y={1};z={2};v={3};type={4}&fm={5}", tiles[i, j].Column, tiles[i, j].Row, Level, web_version, WebMap.ToString(), (int)WebMap);
                    String fileName = cache_url + string.Format("{0:000}", Level) + @"\" + string.Format("u=x={0};y={1};z={2};v={3};type={4}&fm=44", tiles[i, j].Column, tiles[i, j].Row, Level, web_version, WebMap) + @".png";
                    MemoryStream ms;
                    switch (MapCache)
                    {
                        case CacheType.Server:
                            ms = StreamFromUrl(urls[i, j]);
                            imgSet[i, j].Source = Stream2Img(ms);
                            break;
                        case CacheType.Cache:
                            imgSet[i, j].Source = new BitmapImage(new Uri(fileName));
                            break;
                        case CacheType.CacheAndServer:
                            if (File.Exists(fileName))
                            {
                                imgSet[i, j].Source = new BitmapImage(new Uri(fileName));
                            }
                            else
                            {
                                ms = StreamFromUrl(urls[i, j]);
                                imgSet[i, j].Source = Stream2Img(ms);
                                SaveMap(ms, fileName);
                            }
                            break;
                        default:
                            if (File.Exists(fileName))
                            {
                                imgSet[i, j].Source = new BitmapImage(new Uri(fileName));
                            }
                            else
                            {
                                ms = StreamFromUrl(urls[i, j]);
                                imgSet[i, j].Source = Stream2Img(ms);
                                SaveMap(ms, fileName);
                            }
                            break;
                    }
                }


            }
        }

        public void ShowGrid()
        {
            W = this.Width;
            H = this.Height;
            PixelPoint CenterPixelPoint = Core.Mercator2Pixel(CenterMercatorPoint, Level);
            if (layers_manager.Contains(LayerType.Grid))
            {
                layers_manager.Remove(LayerType.Grid);
            }
            else
            {
                layers_manager.Add(LayerType.Grid);
            }

            TileMatrix tileMatrix = new TileMatrix(W, H, CenterPixelPoint);
            Rectangle[,] rectSet = new Rectangle[tileMatrix.Width, tileMatrix.Height];
            Tile[,] matrixSet = new Tile[tileMatrix.Width, tileMatrix.Height];

            for (int i = 0; i < tileMatrix.Width; i++)
            {
                for (int j = 0; j < tileMatrix.Height; j++)
                {
                    matrixSet[i, j] = new Tile(tileMatrix.MinX + i, tileMatrix.MinY + j);
                    double x = (matrixSet[i, j].Column) * TILESIZE - CenterPixelPoint.X + W / 2;
                    double y = -(matrixSet[i, j].Row + 1) * TILESIZE + CenterPixelPoint.Y + H / 2;
                    //生成瓦片网格
                    rectSet[i, j] = new Rectangle();
                    rectSet[i, j].Width = TILESIZE;
                    rectSet[i, j].Height = TILESIZE;
                    rectSet[i, j].Stroke = Brushes.Red;
                    layers_manager.Items(LayerType.Grid).Add(rectSet[i, j], x, y);
                }
            }

        }

        public void SaveMap(MemoryStream ms, String fileName)
        {
            File.WriteAllBytes(fileName, ms.ToArray());
        }

        /// <summary>
        /// 获取当前网址返回的流数据
        /// </summary>
        /// <param name="url"></param>
        /// <returns></returns>
        MemoryStream StreamFromUrl(String url)
        {
            //httpWebRequest构造函数属protected,不能实例化，应通过WebRequest的静态方法获得
            HttpWebRequest httpReq = (HttpWebRequest)WebRequest.Create(url);
            //返回来自Internet的响应
            HttpWebResponse httpRes = (HttpWebResponse)httpReq.GetResponse();
            //获得该响应的Stream,该stream就好比一个水龙头，只知道头，不知道尾，无法直接获取其长度
            //可通过httpRes.ContentLength获取长度,故一般不要直接操作该stream;
            Stream st = httpRes.GetResponseStream();
            MemoryStream ms = new MemoryStream();
            int bits;
            while ((bits = st.ReadByte()) != -1)
            {
                ms.WriteByte((byte)bits);
            }
            //MSDN: HttpWebResponse.Close() 方法关闭响应流并释放到资源的连接以供其他请求重用。 
            httpRes.Close();//一定要关闭连接，连接关闭后，该fs也同时关闭
            return ms;
        }

        BitmapSource Stream2Img(MemoryStream ms)
        {
            ms.Position = 0;//操作内存流时必须注意，可能已被读取，故每次必须置0
            int imgType = ms.ReadByte();
            ms.Position = 0;
            BitmapSource img = null;

            //static readonly byte[] pngHeader = { 0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A };
            //static readonly byte[] jpgHeader = { 0xFF, 0xD8, 0xFF };
            //static readonly byte[] gifHeader = { 0x47, 0x49, 0x46 };
            //static readonly byte[] bmpHeader = { 0x42, 0x4D };
            switch (imgType)
            {
                // PNG: 0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A
                case 0x89:
                    {
                        BitmapDecoder decoder = new PngBitmapDecoder(ms, BitmapCreateOptions.IgnoreColorProfile, BitmapCacheOption.OnLoad);
                        img = decoder.Frames[0];//ImageFrame继承自BitmapSource
                        decoder = null;
                        return img;
                    }

                // JPG: 0xFF, 0xD8, 0xFF
                case 0xFF:
                    {
                        BitmapDecoder decoder = new JpegBitmapDecoder(ms, BitmapCreateOptions.IgnoreColorProfile, BitmapCacheOption.OnLoad);
                        img = decoder.Frames[0];
                        decoder = null;
                        return img;
                    }


                // GIF: 0x47, 0x49, 0x46
                case 0x47:
                    {
                        BitmapDecoder decoder = new GifBitmapDecoder(ms, BitmapCreateOptions.IgnoreColorProfile, BitmapCacheOption.OnLoad);
                        img = decoder.Frames[0];
                        decoder = null;
                        return img;
                    }

                // BMP: 0x42, 0x4D
                case 0x42:
                    {
                        BitmapDecoder decoder = new BmpBitmapDecoder(ms, BitmapCreateOptions.IgnoreColorProfile, BitmapCacheOption.OnLoad);
                        img = decoder.Frames[0];
                        decoder = null;
                        return img;
                    }

                // TIFF: 0x49, 0x49 || 0x4D, 0x4D
                case 0x49:
                case 0x4D:
                    {
                        BitmapDecoder bitmapDecoder = new TiffBitmapDecoder(ms, BitmapCreateOptions.IgnoreColorProfile, BitmapCacheOption.OnLoad);
                        img = bitmapDecoder.Frames[0];
                        bitmapDecoder = null;
                        return img;
                    }

                default:
                    {
                        Debug.WriteLine("unknown image format: " + imgType);
                        return img;
                    }
            }

        }

        private void Canvas_MouseMove(object sender, MouseEventArgs e)
        {

            #region 测量距离
            //e.LeftButton==MouseButtonState.Released这句很重要，否则，即使单击鼠标，任然会发生移动事件
            if (IsLTClicked == true && e.LeftButton == MouseButtonState.Released && IsSurveying == true && IsMoving == false)
            {
                int length = this.Children.Count;
                for (int i = 0; i < length; i++)
                {
                    UIElement item = this.Children[i];
                    if (Canvas.GetZIndex(item) == 1000 && (item is Line))
                    {
                        this.Children.Remove(item);                        
                        break;
                    }
                }
                Line myline = DrawingLine(mouseLtPoint, e.GetPosition(this));
                Point pt1 = new Point(myline.X1, myline.Y1);
                Point pt2 = new Point(myline.X2, myline.Y2);
                LatLong llA = LocalPoint2LatLong(pt1, CenterMercatorPoint, this.Level);
                LatLong llB = LocalPoint2LatLong(pt2, CenterMercatorPoint, this.Level);
                double dist = Core.Distance(llA, llB);
                distText.Inlines.Clear();
                Canvas.SetLeft(distText, pt2.X + 5);
                Canvas.SetTop(distText, pt2.Y + 5);
                if (dist > 2000)
                {
                    distText.Inlines.Add("总长度" + (dist / 1000).ToString("0.00") + "km");
                    distText.Inlines.Add("\n单击确定地点，双击结束");
                    distText.Inlines.FirstInline.Foreground = Brushes.OrangeRed;
                    distText.Inlines.LastInline.Foreground = Brushes.Gray;
                }
                else
                {
                    distText.Inlines.Add("总长度" + dist.ToString("0.00") + "m");
                    distText.Inlines.Add("\n单击确定地点，双击结束");
                    distText.Inlines.FirstInline.Foreground = Brushes.OrangeRed;
                    distText.Inlines.LastInline.Foreground = Brushes.Gray;
                }
            }
            #endregion

            #region 移动地图
            if (e.LeftButton == MouseButtonState.Pressed && IsMoving == true && IsSurveying == false && IsLTClicked == true)
            {
                double deltaX = e.GetPosition(this).X - mouseLtPoint.X;
                double deltaY = e.GetPosition(this).Y - mouseLtPoint.Y;

                PixelPoint CenterPixelPoint = new PixelPoint(BeforeMoveCenterPoint.X - deltaX, BeforeMoveCenterPoint.Y + deltaY);
                CenterMercatorPoint = Core.Pixel2Mercator(CenterPixelPoint, Level);
                RedrawMap();
                ShowGrid();
                layers_manager.Offset(deltaX, deltaY);
            }
            #endregion
        }

        private void Canvas_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            IsLTClicked = false;
            double dx = Math.Abs(e.GetPosition(this).X - mouseLtPoint.X);
            double dy = Math.Abs(e.GetPosition(this).Y - mouseLtPoint.Y);
            Debug.Print("dx=" + dx.ToString());
            Debug.Print("dy=" + dy.ToString());
            mouseLtPoint = e.GetPosition(this);
            IsLTClicked = true;
            if ((dx > 1 || dy > 1) && IsSurveying == true && IsMoving == false)
            {
                distText.Visibility = Visibility.Visible;
                Popup mypop = new Popup();
                pop = mypop;
                DrawingPoint(mouseLtPoint);
                IsLTClicked = true;
                //将前一次所画图像保存到另一个图层当中去
                int length = this.Children.Count;
                for (int i = 0; i < length; i++)
                {
                    UIElement item = this.Children[i];
                    if (Canvas.GetZIndex(item) == 1000 && (item is Shape))
                    {
                        Canvas.SetZIndex(item, 999);
                    }
                }
                //pop.PlacementTarget = this;
                //pop.Placement = PlacementMode.Top;
                //pop.VerticalOffset = pt2.Y + 5;
                //pop.HorizontalOffset = pt2.X + 5;
                //TextBlock mytext = new TextBlock();
                //mytext.FontFamily = new FontFamily("Cabira");
                //mytext.FontWeight = FontWeights.ExtraBold;
                //mytext.Foreground = Brushes.OrangeRed;
                //mytext.Background = Brushes.LightGray;

                //if (dist > 2000)
                //{
                //    mytext.Text = (dist / 1000).ToString("0.00") + "km";
                //}
                //else
                //{
                //    mytext.Text = dist.ToString("0.00") + "m";
                //}

                //pop.Child = mytext;
                //pop.IsOpen = true;
            }
            if ((dx < 1 && dy < 1) && IsSurveying == true && IsMoving == false)
            {
                Button mybtn = new Button();
                mybtn.Content = "clear";
                this.Children.Add(mybtn);
                Canvas.SetZIndex(mybtn, 999);
                Canvas.SetLeft(mybtn, e.GetPosition(this).X + 5);
                Canvas.SetTop(mybtn, e.GetPosition(this).Y + 5);

                IsSurveying = false;
                IsMoving = true;
                mybtn.Click += new RoutedEventHandler(mybtn_Click);
            }

            if (IsSurveying == false && IsMoving == true)
            {
                //list_pt.Add(e.GetPosition(this));
                BeforeMoveCenterPoint = Core.Mercator2Pixel(CenterMercatorPoint, Level);
                layers_manager.GetHash();
            }
        }
        private void mybtn_Click(object sender, RoutedEventArgs e) 
        {
            for (int i = 0; i < this.Children.Count; i++)
            {
                UIElement item = this.Children[i];
                if (Canvas.GetZIndex(item) >800)
                {
                    Debug.Print("找到一个shape控件");
                    this.Children.Remove(item);
                }
            }
            //this.Children.Remove((Button)sender);
        }

        private void MenuItem_Click(object sender, RoutedEventArgs e)
        {
            if (layers_manager.Contains(LayerType.Marker) == false)
            {
                layers_manager.Add(LayerType.Marker);
            }

            MessageBoxResult msg = MessageBox.Show("是否将该点标注?", "显示标注", MessageBoxButton.OKCancel);
            if (msg == MessageBoxResult.OK)
            {
                Marker marker = new Marker();
                {
                    double x = mouseRtPoint.X;
                    double y = mouseRtPoint.Y;
                    layers_manager.Items(LayerType.Marker).Add(marker, x, y);
                }
            }
        }

        private LatLong LocalPoint2LatLong(Point localPoint, MercatorPoint mp_cc, int level)
        {
            MercatorPoint mp = LocalPoint2MercatorPoint(localPoint, mp_cc, level);
            return Core.Mercator2LatLong(mp);
        }

        private Point LatLong2LocalPoint(LatLong latlong, MercatorPoint mp_cc, int level)
        {
            MercatorPoint mp = Core.LatLong2Mercator(latlong);
            return MercatorPoint2LocalPoint(mp, mp_cc, level);
        }

        public Point MercatorPoint2LocalPoint(MercatorPoint mp, MercatorPoint mp_cc, int level)
        {
            PixelPoint pp = Core.Mercator2Pixel(mp, level);
            PixelPoint cc = Core.Mercator2Pixel(mp_cc, level);
            W = this.Width;
            H = this.Height;
            double x = pp.X + (-cc.X + 0.5 * W);
            double y = -pp.Y + (cc.Y + 0.5 * H);
            return new Point(x, y);
        }

        public Point PixelPoint2LocalPoint(PixelPoint pp, PixelPoint cc)
        {
            W = this.Width;
            H = this.Height;
            double x = pp.X + (-cc.X + 0.5 * W);
            double y = -pp.Y + (cc.Y + 0.5 * H);
            return new Point(x, y);
        }

        public MercatorPoint LocalPoint2MercatorPoint(Point localPoint, MercatorPoint mp_cc, int level)
        {
            PixelPoint cc = Core.Mercator2Pixel(mp_cc, level);
            W = this.Width;
            H = this.Height;
            double x = localPoint.X + (cc.X - 0.5 * W);
            double y = -localPoint.Y + (cc.Y + 0.5 * H);
            return Core.Pixel2Mercator(new PixelPoint(x, y), level);
        }

        public PixelPoint LocalPoint2PixelPoint(Point localPoint, PixelPoint cc)
        {
            W = this.Width;
            H = this.Height;
            double x = localPoint.X + (cc.X - 0.5 * W);
            double y = -localPoint.Y + (cc.Y + 0.5 * H);
            return new PixelPoint(x, y);
        }

        //public ObservableCollection<Marker> Markers = new ObservableCollection<Marker>();
        //public static readonly DependencyProperty MapProviderProperty = DependencyProperty.Register("MapProvider", typeof(String), typeof(MapContainer), new UIPropertyMetadata("123"));
        //public String MapProvider
        //{
        //    get { return (String)GetValue(MapProviderProperty); }
        //    set { SetValue(MapProviderProperty, value); }
        //}
        public void Zoom(int newlevel)
        {
            FrameworkElement item;
            int length = this.Children.Count;
            for (int i = 0; i < length; i++)
            {
                item = (FrameworkElement)this.Children[i];
                if (Canvas.GetZIndex(item) > (int)LayerType.Grid)
                {
                    //注意图钉尾端的位置和控件的位置有一定区别
                    Point pt = new Point(Canvas.GetLeft(item) + 0.25 * item.Width, Canvas.GetTop(item) + item.Height);
                    MercatorPoint mp = LocalPoint2MercatorPoint(pt, CenterMercatorPoint, this.Level);
                    pt = MercatorPoint2LocalPoint(mp, CenterMercatorPoint, newlevel);
                    Canvas.SetLeft(item, pt.X - 0.25 * item.Width);
                    Canvas.SetTop(item, pt.Y - item.Height);
                }
            }
            Level = newlevel;
            RedrawMap();
        }

        private Line DrawingLine(Point startPt, Point endPt)
        {
            Line myLine = new Line();
            myLine.X1 = startPt.X;
            myLine.Y1 = startPt.Y;
            myLine.X2 = endPt.X;
            myLine.Y2 = endPt.Y;
            myLine.StrokeThickness = 2;
            myLine.Stroke = Brushes.Red;
            Canvas.SetZIndex(myLine, 1000);
            Canvas.SetLeft(myLine, 0);
            Canvas.SetTop(myLine, 0);
            this.Children.Add(myLine);
            return myLine;

            //LineGeometry myLineGeometry = new LineGeometry();
            //myLineGeometry.StartPoint = startPt;
            //myLineGeometry.EndPoint = endPt;
            //Path myPath = new Path();
            //myPath.Stroke = Brushes.Red;
            //myPath.StrokeThickness = 2;
            //myPath.Data = myLineGeometry;
            //Canvas.SetZIndex(myPath,1000);
            //this.Children.Add(myPath);
        }

        private void DrawingPoint(Point pt)
        {
            EllipseGeometry myEllipseGeometry = new EllipseGeometry();
            myEllipseGeometry.Center = pt;
            myEllipseGeometry.RadiusX = 3;
            myEllipseGeometry.RadiusY = 3;
            Path myPath = new Path();
            myPath.Stroke = Brushes.Red;
            myPath.StrokeThickness = 2;
            myPath.Data = myEllipseGeometry;
            Canvas.SetZIndex(myPath, 1000);
            //此处必须设置myPath的左右位置，否则移动不了，即移动时其对应的左右坐标位置NaN
            Canvas.SetLeft(myPath,0);
            Canvas.SetTop(myPath,0);
            this.Children.Add(myPath);
        }
    }



}
